/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.core.output; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.io.PipedReader; import java.io.PipedWriter; import java.io.ObjectInput; import java.io.IOException; import java.util.*; import javax.swing.JComponent; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.plaf.TabbedPaneUI; import javax.swing.border.Border; import org.openide.awt.SplittedPanel; import org.openide.awt.MouseUtils; import org.openide.TopManager; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.windows.*; import org.netbeans.core.windows.WellKnownModeNames; /* TODO: - setOutputVisible - err editor init only if err separated - ??? known bug: exception on setInputVisible(false) */ /** One Output window's tab. Can contain StdOut and ErrOut "editors" and StdIn input line. * Initially, it contains just the StdOut "editor". * This class is final only for performance reasons. Can be unfinaled * happily. * * @author Petr Hamernik, Ian Formanek, Jan Jancura */ public final class OutputTab extends TopComponent implements InputOutput, java.io.Externalizable, PropertyChangeListener, WellKnownModeNames { /** generated Serialized Version UID */ static final long serialVersionUID = 9220615777783507203L; public static final int DEFAULT_WINDOW_HEIGHT = 180; public static final int TYPICAL_WINDOWS_TASKBAR_HEIGHT = 27; public static final String ICON_RESOURCE = "/org/netbeans/core/resources/frames/output.gif"; // NOI18N /** Mapping of string:OutputTab */ static WeakHashMap ioCache = new WeakHashMap(7); private SplittedPanel splittedInside; private int splitTypeBackup = SplittedPanel.HORIZONTAL; /** If true, the error output is separated from the normal output */ private boolean errSeparated; private boolean inVisible; private boolean errVisible; /** Should this InputOutput take the focus and bring the window to the front, * when anything is written into the stream. */ private boolean focusTaken = false; /** The reader for standard input */ private java.io.Reader inReader; private JTextField inputLine; private JLabel inputLineDesc; private JPanel inputPanel; /** Output pane */ private OutPane output; /** Error pane */ private OutPane error; /** am I activated */ private boolean amISelected; /** piped writer */ private PipedWriter inWriter = new PipedWriter(); /** Creates an empty Output window with only the std output pane and with * reference to its pane. */ public OutputTab() { this(""); // NOI18N } /** Creates an empty Output window with only the std output pane */ public OutputTab(String name) { synchronized (OutputTab.class) { setName (name); setActivatedNodes (null); errSeparated = false; setLayout(new BorderLayout()); splittedInside = new SplittedPanel(); add (splittedInside, "Center"); // NOI18N output = new OutPane(this); error = new OutPane(this); JScrollPane opane = new JScrollPane(output); JScrollPane errpane = new JScrollPane(error); splittedInside.add(opane, SplittedPanel.ADD_FIRST); splittedInside.add(errpane, SplittedPanel.ADD_SECOND); errVisible = false; splittedInside.setSplitType(SplittedPanel.NONE); try { inReader = new OutputPipedReader(inWriter); } catch (java.io.IOException ex) { TopManager.getDefault().notifyException(ex); } inputLine = new JTextField(); inputLine.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { inWriter.write(inputLine.getText()+"\n"); // NOI18N inWriter.flush(); } catch (java.io.IOException ex) { // TopManager.getDefault().notifyException(ex); } inputLine.setText(""); // NOI18N } }); inputLineDesc = new JLabel(OutputSettings.getString("CTL_ProgramInput")); inputLineDesc.setForeground(java.awt.Color.black); JPanel labelPanel = new JPanel(); labelPanel.setLayout(new GridBagLayout()); inputPanel = new JPanel(); inputPanel.setLayout(new GridBagLayout()); GridBagConstraints gd = new GridBagConstraints(); gd.anchor = GridBagConstraints.WEST; gd.fill = GridBagConstraints.NONE; gd.gridwidth = 1; gd.gridheight = 1; gd.gridx = 0; gd.gridy = 0; gd.weightx = 0; gd.weighty = 1; inputPanel.add(labelPanel, gd); gd = new GridBagConstraints(); gd.anchor = GridBagConstraints.WEST; gd.fill = GridBagConstraints.NONE; gd.gridwidth = 1; gd.gridheight = 1; gd.gridx = 0; gd.gridy = 0; gd.weightx = 0; gd.weighty = 1; gd.insets = new java.awt.Insets(0, 8, 0, 8); labelPanel.add(inputLineDesc, gd); gd = new GridBagConstraints(); gd.anchor = GridBagConstraints.WEST; gd.fill = GridBagConstraints.HORIZONTAL; gd.gridwidth = 2; gd.gridheight = 1; gd.gridx = 1; gd.gridy = 0; gd.weightx = 1; gd.weighty = 1; inputPanel.add(inputLine, gd); inVisible = false; OutputSettings set = (OutputSettings)OutputSettings.findObject (OutputSettings.class, true); set.addPropertyChangeListener (this); ioCache.put(name, this); } } public HelpCtx getHelpCtx () { return new HelpCtx (OutputTab.class); } /** Activates the component */ protected void componentActivated () { amISelected = true; inputLine.requestFocus(); } /** Deactivates the component */ protected void componentDeactivated () { amISelected = false; } /** Listener for changes of the property. */ public void propertyChange (PropertyChangeEvent ev) { textPropertiesChanged(); invalidate (); } /** A class that works same as PipedReader and in advance shows the InputLine * on the read request */ class OutputPipedReader extends PipedReader { OutputPipedReader(PipedWriter writer) throws java.io.IOException { super(writer); } public int read(char cbuf[], int off, int len) throws java.io.IOException { setInputVisible(true); return super.read(cbuf, off, len); } public int read() throws java.io.IOException { setInputVisible(true); return super.read(); } } /** Method to acquire an OutputWriter for accessing an output pane of the tab. * @return an OutputWriter for accessing an output pane of the tab. */ public OutputWriter getOut() { return output.writer; } /** Method to acquire an error output pane of the tab. * @return an error output pane of the tab. */ public OutputWriter getErr() { if (errSeparated) return error.writer; else return output.writer; } /** Method to acquire a Reader for accessing an input line of the tab. * On a first try to read from the Reader, an input line should be * added to this tab in the OutputWindow if it does not exist yet. * @return a Reader for accessing an input line of the tab. */ public java.io.Reader getIn() { return inReader; } /** Closes I/O */ public void closeInputOutput () { Runnable run = new Runnable() { public void run() { close(); } }; java.awt.EventQueue.invokeLater(run); try { inWriter.flush(); inWriter.close(); } catch (java.io.IOException ex) { } } /** checks whether tab is closed or not */ public boolean isClosed() { return true; } /** Shows or hides the std output pane. * @param value the new visibility state of the std output "editor" */ public void setOutputVisible(boolean value) { // [PENDING] } /** Shows or hides the err output pane (regardless the error output * is separated or not). * @param value the new visibility state of the err output "editor" */ public void setErrVisible(boolean value) { if ((errVisible == value) || (!errSeparated)) return; errVisible = value; if (errVisible) splittedInside.setSplitType(splitTypeBackup); else { splitTypeBackup = splittedInside.getSplitType(); if (splitTypeBackup == SplittedPanel.NONE) splitTypeBackup = SplittedPanel.HORIZONTAL; splittedInside.setSplitType(SplittedPanel.NONE); } invalidate (); Container parent = getParent (); if (parent != null) parent.validate (); } /** Shows or hides the input line * @param value the new visibility state of the input line */ public void setInputVisible(boolean value) { if (inVisible == value) return; inVisible = value; if (inVisible) { add (inputPanel, "South"); // NOI18N if (isShowing () && amISelected) { // select focus inputLine.requestFocus (); } if (isClosed() && isFocusTaken()) { open(); } } else { remove(inputPanel); } inputPanel.invalidate(); inputLine.invalidate(); inputLineDesc.invalidate(); Container parent = getParent (); if (parent != null) parent.validate (); } public boolean isErrSeparated() { return errSeparated; } public void setErrSeparated(boolean value) { if (errSeparated == value) return; errSeparated = value; if (!errSeparated) setErrVisible(false); } /** @return true if output window takes focus, when anything is written. */ public boolean isFocusTaken() { return focusTaken; } /** Set true, if you want to focus out window, when anything is written * into the stream */ public void setFocusTaken(boolean value) { focusTaken = value; } /** * Sets this input / output visible. In the implementation where InputOutput * is representated by one tab in TabbedPane selects this pane for example. */ public void select () { // [JST] hopefully there should be true and not focusTaken requestFocus (); inputLine.requestFocus(); } /** Request focus for the output pane */ public void requestFocus () { super.requestFocus (); output.requestFocus (); } /** always open this top component in output mode, if * no mode for this component is specified yet */ public void open (Workspace workspace) { Workspace realWorkspace = (workspace == null) ? TopManager.getDefault().getWindowManager().getCurrentWorkspace() : workspace; // dock into outwin mode if not docked yet Mode mode = realWorkspace.findMode(OUTPUT); if (mode == null) { mode = realWorkspace.createMode(OUTPUT, getOutDisplayName(), OutputTab.class.getResource(ICON_RESOURCE)); } Mode tcMode = realWorkspace.findMode(this); if (tcMode == null) mode.dockInto(this); // behave like superclass super.open(workspace); } /** @return true if the requestor is out (not err) and this tab is selected */ boolean amISelected(OutPane requestor) { return (requestor == output) && amISelected; } /** Checks if the Next- and Prev-OutJumpActions should be enabled or disabled. */ void checkNextPrevActions() { output.checkNextPrevActions(); } /** Notification about the change in output colors or font */ void textPropertiesChanged() { output.repaint (); error.repaint (); } /** rebinds this tab to ow */ void rebindTab() { open (); } /** Creates new instance into inWriter because of pending data in the old one * @return flushed Reader */ public final java.io.Reader flushReader() { inWriter = new PipedWriter(); try { inReader = new OutputPipedReader(inWriter); } catch (java.io.IOException ex) { TopManager.getDefault().notifyException(ex); return null; } return inReader; } // OutputTabs are not serialized // null is returned during deserialization // only standard output tab is dseserialized to // default instance public synchronized Object writeReplace() throws java.io.ObjectStreamException { if (replace == null) { replace = new Replace(this.equals(panel)); } return replace; } private Replace replace; /** This class is serializaed instead of Output tab */ static class Replace implements java.io.Serializable { /** true if output tab was default one, false otherwise */ boolean defaultInstance; static final long serialVersionUID =-7661588925874641544L; public Replace (boolean defaultInstance) { this.defaultInstance = defaultInstance; } /** Resolve as default singleton or null */ public Object readResolve() throws java.io.ObjectStreamException { return defaultInstance ? getStdOutputTab() : null; } } // // Static stuff // /** sinngleton instance of the standard output tab */ private static OutputTab panel; private static synchronized void initialize () { if (panel == null) { // create the tab for StdOut panel = new OutputTab(NbBundle.getBundle(OutputTab.class).getString("CTL_OutputWindow_OutputTab")); } } public static InputOutput getIO(String name, boolean newIO) { initialize (); if (newIO) { return new OutputTab(name); } else { InputOutput ino = (InputOutput) ioCache.get(name); if (ino == null) { ino = new OutputTab(name); } return ino; } } public static OutputWriter getStdOut() { initialize (); return panel.getOut(); } /** Returns standard output top component */ public static OutputTab getStdOutputTab() { initialize (); return panel; } static String getOutDisplayName() { return org.openide.util.NbBundle.getBundle("org.netbeans.core.windows.Bundle").getString("CTL_OutputWindow"); } } /* * Log * 35 Gandalf 1.34 2/17/00 Ian Formanek Minor UI improvement - * got rid of BevelBorder for "Program Input" label in the output window * 34 Gandalf 1.33 2/15/00 David Simonek open method updated * 33 Gandalf 1.32 1/12/00 Ales Novak i18n * 32 Gandalf 1.31 1/12/00 Ales Novak open() discarded * 31 Gandalf 1.30 1/3/00 Radko Najman New property value * applied immediately * 30 Gandalf 1.29 12/15/99 Ales Novak program input made * visible according to Kartik Mithail * 29 Gandalf 1.28 12/3/99 Jaroslav Tulach no selected nodes. * 28 Gandalf 1.27 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 27 Gandalf 1.26 8/17/99 Ian Formanek Generated serial version * UID * 26 Gandalf 1.25 7/29/99 David Simonek further ws serialization * changes * 25 Gandalf 1.24 7/22/99 Ales Novak caching of InputOutputs * 24 Gandalf 1.23 7/19/99 Jesse Glick Context help. * 23 Gandalf 1.22 7/14/99 Ales Novak sync bug * 22 Gandalf 1.21 7/14/99 Ales Novak new win sys * 21 Gandalf 1.20 7/12/99 Jesse Glick Context help. * 20 Gandalf 1.19 7/11/99 David Simonek window system change... * 19 Gandalf 1.18 6/18/99 Ales Novak IOException catched and * quietly discarded * 18 Gandalf 1.17 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 17 Gandalf 1.16 5/13/99 Ales Novak VERTICAL_SCROLLBAR_ALWAYS * removed * 16 Gandalf 1.15 5/12/99 Ales Novak serialization * 15 Gandalf 1.14 5/11/99 David Simonek changes to made window * system correctly serializable * 14 Gandalf 1.13 5/5/99 Ales Novak scrollbars fixed * 13 Gandalf 1.12 5/2/99 Ian Formanek Default output window * height raised from 150 to 180. * 12 Gandalf 1.11 4/9/99 Ales Novak output was not shown * even if reading * 11 Gandalf 1.10 4/9/99 Ales Novak * 10 Gandalf 1.9 3/25/99 David Simonek changes in window * system, initial positions, bugfixes * 9 Gandalf 1.8 3/19/99 Jaroslav Tulach Deleted OutWindow * 8 Gandalf 1.7 3/19/99 Ian Formanek Added default bounds * 7 Gandalf 1.6 3/19/99 David Simonek * 6 Gandalf 1.5 3/17/99 David Simonek slightly changed window * system * 5 Gandalf 1.4 3/12/99 David Simonek default mode set to * OUTPUT * 4 Gandalf 1.3 3/12/99 Jan Jancura * 3 Gandalf 1.2 1/6/99 Jaroslav Tulach * 2 Gandalf 1.1 1/6/99 Ian Formanek Reflecting changes in * location of package "awt" * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ * Beta Change History: * 0 Tuborg 0.20 --/--/98 Petr Hamernik Completly redesigned whole package (switch to new editor) * 0 Tuborg 0.21 --/--/98 Petr Hamernik Stealing of focus is optional * 0 Tuborg 0.22 --/--/98 Petr Hamernik colors * 0 Tuborg 0.24 --/--/98 Jan Jancura Tab switching changed * 0 Tuborg 0.25 --/--/98 Ales Novak bugfix * 0 Tuborg 0.26 --/--/98 Jaroslav Tulach select calls setOut (this, true) * 0 Tuborg 0.27 --/--/98 Jaroslav Tulach requestFocus */